home *** CD-ROM | disk | FTP | other *** search
- /*
- * nntp communications functions
- */
-
- #include "conf.h"
- #include <sys/types.h>
- #include <errno.h>
- #include <ctype.h>
- #include <setjmp.h>
- #include <signal.h>
- #ifdef HAVE_STRING_H
- #include <string.h>
- #else
- #include <strings.h>
- #endif
- #ifdef FAKESYSLOG
- #include "fsyslog.h"
- #else
- #include <syslog.h>
- #endif
- #include <sys/socket.h>
- #ifdef MMAP
- #include <sys/stat.h>
- #include <sys/mman.h>
- #endif
- #include <sys/time.h>
- #include "readline.h"
- #include "nntplink.h"
- #include "nntp.h"
- #include "strfuns.h"
-
- extern Boolean Abort_signaled;
- extern Boolean Debug;
- extern int Input_from;
- extern Boolean Log_close;
- extern long Idle_time;
- extern int Dtablesize;
-
- extern void fail();
- extern void log();
-
- extern char *E_fdopen;
-
- void close_connection();
-
- #ifndef HAVE_SELECT
- SIGRET to_read_reply();
- static jmp_buf RRstack;
- int ignore_sigalrm = TRUE;
- #endif
-
- FileBuf *rmt_rd_fbp;
- FILE *rmt_wr_fp;
-
- /*
- * send cmd to remote, terminated with a CRLF.
- */
- Boolean
- send_command(command, log_error)
- char *command;
- Boolean log_error;
- {
- static char *fname = "send_command: ";
-
- dlog(LOG_DEBUG, "", "%s>>> %s\n", command);
-
- Idle_time = 0L;
-
- (void) fprintf(rmt_wr_fp, "%s\r\n", command);
- (void) fflush(rmt_wr_fp);
-
- if (ferror(rmt_wr_fp)) {
- if (log_error)
- log(LOG_WARNING, fname, "%s%s: error sending command: %s\n",
- Host.name, errmsg(errno));
- return FALSE;
- }
- return TRUE;
- }
-
-
- #ifndef HAVE_SELECT
- SIGRET
- to_read_reply()
- {
- #ifdef BROKEN_SIGNAL
- signal(SIGALRM, SIG_IGN);
- signal(SIGALRM, to_read_reply);
- #endif
-
- if (!ignore_sigalrm)
- LONGJMP(RRstack, 1);
-
- }
- #endif /* !HAVE_SELECT */
-
-
- /**
- ** read a reply line from the remote server and return the code number
- ** as an integer, and the message in a buffer supplied by the caller.
- ** Returns NULL if something went wrong.
- **/
- char *
- read_reply(resp)
- int *resp;
- {
- static char *fname = "read_reply: ";
- char *cp;
- char *reply;
- unsigned int rep_len;
- #ifdef HAVE_SELECT
- int cnt;
- fd_set readfds;
- static struct timeval timeout = {0, 0};
- #endif
-
- *resp = FAIL; /* assume we're gonna fail */
-
- #ifdef HAVE_SELECT
-
- timeout.tv_sec = READ_TIMEOUT;
- FD_ZERO (&readfds);
- FD_SET (fb_fileno(rmt_rd_fbp), &readfds);
- if ((cnt = select(Dtablesize, &readfds, 0, 0, &timeout)) == 0) {
- log(LOG_WARNING, fname,
- "%s%s: connection timed out while reading reply\n", Host.name);
- return NULL;
- } else if (cnt < 0) {
- log(LOG_WARNING, fname,
- "%s%s: select() error while reading reply: %s",
- Host.name, errmsg(errno));
- return NULL;
- }
-
- reply = fb_readline(rmt_rd_fbp, &rep_len);
- if (fb_err(rmt_rd_fbp))
- log(LOG_WARNING, fname, "%s%s: read() error reading reply: %s\n",
- Host.name, errmsg(errno));
-
- #else
- if (SETJMP(RRstack)) {
- (void) alarm(0); /* reset alarm clock */
- errno = ETIMEDOUT; /* connection timed out */
- log(LOG_WARNING, fname,
- "%s%s: connection timed out while reading reply\n",
- Host.name);
- return NULL; /* bad read, remote time out */
- }
-
- ignore_sigalrm = FALSE;
- (void) alarm(READ_TIMEOUT);
-
- reply = fb_readline(rmt_rd_fbp, &rep_len);
- if (fb_err(rmt_rd_fbp))
- log(LOG_WARNING, fname, "%s%s: error reading reply: %s\n",
- Host.name, errmsg(errno));
-
- ignore_sigalrm = TRUE;
- (void) alarm(0); /* reset alarm clock */
-
- #endif /* HAVE_SELECT */
-
- if (reply == NULL)
- return NULL;
-
- /*
- * Make sure that what the remote sent us had a CRLF at the end
- * of the line, and then null it out.
- */
- if (rep_len > 2 && *(cp = &reply[rep_len - 1]) == '\r')
- *cp = '\0';
- else {
- log(LOG_WARNING, fname, "%s%s: bad reply from remote: %s\n",
- Host.name, reply);
- return NULL; /* error reading from remote */
- }
-
- dlog(LOG_DEBUG, "", "%s%s\n", reply);
-
- /*
- * Skip any non-digits leading the response code
- * and then convert the code from ascii to integer for
- * return from this routine.
- */
- cp = reply;
- while(*cp != '\0' && isascii(*cp) && !isdigit(*cp))
- cp++; /* skip anything leading */
-
- if (*cp == '\0' || !isascii(*cp)) {
- log(LOG_WARNING, fname, "%s%s: No response code in reply: %s\n",
- Host.name, reply);
- return NULL; /* error reading from remote */
- }
-
- *resp = atoi(cp);
-
- return reply;
- }
-
-
- char *
- converse(line, resp)
- char *line;
- int *resp;
- {
- char *reply;
-
- if (!send_command(line, DO_LOG_ERROR))
- return NULL;
-
- while(((reply = read_reply(resp)) != NULL) &&
- (*resp >= 100) && (*resp < 200))
- ;
-
- if (*resp == FAIL)
- close_connection(!SEND_QUIT_MSG);
-
- return reply;
- }
-
-
- /*
- * open_connection - Contact the remote server and set up the two global
- * FILE pointers to that descriptor.
- *
- */
- Boolean
- open_connection(remote_server, transport)
- char *remote_server;
- int transport;
- {
- static char *fname = "open_connection: ";
-
- int socket0, socket1;
- char *reply;
- int resp;
-
- switch(transport) {
- case T_IP_TCP:
- socket0 = get_tcp_conn(remote_server, "nntp");
- break;
-
- case T_DKHOST:
- #ifdef DKHOST
- socket0 = get_dk_conn(remote_server);
- #else
- fail(fname, "%sno DKHOST support compiled in\n");
- #endif
- break;
-
- case T_DECNET:
- #ifdef DECNET
- (void) signal(SIGPIPE, SIG_IGN);
- socket0 = dnet_conn(remote_server, "NNTP", SOCK_STREAM, 0, 0, 0, 0);
- if (socket0 < 0) {
- switch(errno) {
- case EADDRNOTAVAIL:
- socket0 = NOHOST;
- break;
- case ESRCH:
- socket0 = NOSERVICE;
- break;
- }
- }
- #else
- fail(fname, "%sno DECNET support compiled in\n");
- #endif
- break;
- }
-
- if (socket0 < 0) {
- switch(socket0) {
- case NOHOST:
- log(LOG_WARNING, fname, "%s%s: host unknown\n", remote_server);
- return FALSE;
- case NOSERVICE:
- log(LOG_WARNING, fname, "%s%s: service unknown: nntp\n",
- remote_server);
- return FALSE;
- case FAIL:
- log(LOG_NOTICE, fname, "%s%s: socket(): %s\n", remote_server,
- errmsg(errno));
- return FALSE;
- }
- }
-
- if ((socket1 = dup(socket0)) < 0) {
- log(LOG_WARNING, fname, "%s%s: dup(%d): %s\n", remote_server, socket0,
- errmsg(errno));
- CLOSE(socket0);
- return FALSE;
- }
-
- if ((rmt_wr_fp = fdopen(socket1, "w")) == NULL) {
- log(LOG_WARNING, fname, E_fdopen, remote_server, socket1, "w",
- errmsg(errno));
- CLOSE(socket0);
- CLOSE(socket1);
- return FALSE;
- }
-
- rmt_rd_fbp = fb_fdopen(socket0);
-
- Time.begin_real = time(NULL);
-
- reply = read_reply(&resp);
- ++Stats.connects;
-
- switch(resp) {
- case OK_CANPOST:
- case OK_NOPOST:
- break;
-
- default:
- if (reply != NULL)
- log(LOG_NOTICE, fname, "%s%s: greeted us with %s\n", remote_server,
- reply);
- CLOSE(socket0);
- fb_close(rmt_rd_fbp);
- FCLOSE(rmt_wr_fp);
- return FALSE;
- }
-
- Host.connected = TRUE;
- Idle_time = 0L;
- return TRUE;
- }
-
-
- /*
- * close the connection with the remote server.
- *
- * We trap SIGPIPE because the socket might already be gone.
- */
- void
- close_connection(send_quit)
- Boolean send_quit;
- {
- register SIGRET (*pstate)() = signal(SIGPIPE, SIG_IGN);
- int resp;
-
- Host.connected = FALSE;
- Stats.since_close = 0L;
-
- if (rmt_wr_fp == NULL)
- return;
-
- if (send_quit && !send_command("QUIT", DONT_LOG_ERROR))
- /*
- * I don't care what they say to me; this is just being polite.
- */
- (void) read_reply(&resp);
-
- CLOSE(fb_fileno(rmt_rd_fbp));
- fb_close(rmt_rd_fbp);
- FCLOSE(rmt_wr_fp);
- (void) signal(SIGPIPE, pstate);
-
- Time.end_real = time(NULL);
- Time.elapsed = Time.end_real - Time.begin_real;
- Time.begin_real = 0;
-
- if (Log_close)
- log_stats();
-
- return;
- }
-
- /**
- ** send the contents of an open file descriptor to the remote,
- ** with appropriate RFC822 filtering (e.g. CRLF line termination,
- ** and dot escaping). Return FALSE if something went wrong.
- **/
- Boolean
- send_connection(fbp)
- FileBuf *fbp;
- {
- register char *cp;
- static char *fname = "send_connection: ";
-
- #ifdef MMAP
- register int c;
- register int nl = TRUE; /* assume we start on a new line */
- register char *mbufr, *mptr;
- long msize;
- struct stat sbuf;
- #endif /* MMAP */
-
- /*
- ** I'm using putc() instead of fputc();
- ** why do a subroutine call when you don't have to?
- ** Besides, this ought to give the C preprocessor a work-out.
- */
- #ifdef MMAP
- #define PUTC(c) if (putc(c, rmt_wr_fp) == EOF) {\
- (void) munmap (mbufr, sbuf.st_size);\
- return FALSE; }
- #endif /* MMAP */
-
- if (fbp == NULL)
- return FALSE;
-
- #ifdef MMAP
- /* map the article into memory */
- (void) fstat(fb_fileno(fbp), &sbuf);
- mbufr = mmap (0, sbuf.st_size, PROT_READ, MAP_PRIVATE,
- fb_fileno(Article.fbp), 0);
- if(mbufr == (char *) -1)
- return send_command(".", DO_LOG_ERROR);
-
- Idle_time = 0L;
-
- mptr = mbufr; /* start of article in memory */
- msize = sbuf.st_size; /* size of article (bytes) */
- while(msize-- > 0) {
- c = *mptr++;
- switch(c) {
- case '\n':
- PUTC('\r'); /* \n -> \r\n */
- PUTC(c);
- nl = TRUE; /* for dot escaping */
- break;
-
- case '.':
- if (nl) {
- PUTC(c); /* add a dot */
- nl = FALSE;
- }
- PUTC(c);
- break;
-
- default:
- PUTC(c);
- nl = FALSE;
- break;
- }
- }
-
- if (!nl) {
- PUTC('\r');
- PUTC('\n');
- }
- (void) munmap (mbufr, sbuf.st_size);
-
- #else /* !MMAP */
-
- Idle_time = 0L;
-
- while((cp = fb_readline(fbp, NULL)) != NULL) {
- if (*cp == '.')
- putc('.', rmt_wr_fp);
- if ((fputs(cp, rmt_wr_fp) == EOF) || (fputs("\r\n", rmt_wr_fp) == EOF))
- return FALSE;
- }
-
- if (fb_error(fbp))
- log(LOG_WARNING, fname, "%s%s: error reading %s: %s\n",
- Host.name, Article.filename, errmsg(errno));
-
- if (fflush(rmt_wr_fp) != 0)
- return FALSE;
-
- #endif /* MMAP */
-
- return send_command(".", DO_LOG_ERROR);
- }
-